/*
 * BSM6.0
 *
 * Copyright 2012-2013 (C) SINODATA CO., LTD. All Rights Reserved.
 */
package com.sinodata.bsm.cicp.task;

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;

import com.sinodata.bsm.cicp.cache.CacheFactory;
import com.sinodata.bsm.cicp.cache.CollectorCache;
import com.sinodata.bsm.cicp.cache.ResCache;
import com.sinodata.bsm.cicp.service.CollectorService;
import com.sinodata.bsm.cicp.service.ServiceFactory;
import com.sinodata.bsm.common.constants.PropertyConstants;
import com.sinodata.bsm.common.vo.CollectSchedule;
import com.sinodata.bsm.common.vo.CollectTask;
import com.sinodata.bsm.common.vo.Collector;
import com.sinodata.bsm.common.vo.Res;

/**
 * <p>
 * Description: 
 * </p>
 *
 * @author tangli
 * @version 1.0

 * <p>
 * History: 
 *
 * Date                     Author         Version     Description
 * ---------------------------------------------------------------------------------
 * 2012-5-19 上午10:49:34          tangli         1.0         To create
 * </p>
 *
 * @since 
 * @see     
 */
public class TaskManager {
    static final String JOB_KEY_INTERVAL = "interval";

    private static TaskManager instance = new TaskManager();
    private static Logger logger = Logger.getLogger(TaskManager.class);
    private Scheduler scheduler;

    private final ResCache resCache = (ResCache) CacheFactory.getCache(ResCache.class);

    private final CollectorCache collectorCache = (CollectorCache) CacheFactory.getCache(CollectorCache.class);

    private final CollectorService collectorService = (CollectorService) ServiceFactory.get("CollectorService");

    /**
     * 
     */
    private TaskManager() {
        try {
            scheduler = new StdSchedulerFactory("cicp-conf/quartz.properties").getScheduler();
            scheduler.start();
        } catch (SchedulerException e) {
            logger.error("初始化任务管理器失败", e);
        }
    }

    public static TaskManager getInstance() {
        return instance;
    }

    public void initAllTasks() {
        List<Res> resList = resCache.getALL();
        for (Res res : resList) {
            startResTask(res);
        }
    }

    public void stopAllTasks() {
        GroupMatcher<JobKey> gm = GroupMatcher.groupStartsWith("FIXED_");
        try {
            scheduler.getJobKeys(gm);
            List<JobKey> jobKeys = new ArrayList<JobKey>();
            jobKeys.addAll(scheduler.getJobKeys(gm));
            System.out.println("现有任务数：" + scheduler.getJobKeys(gm).size());
            scheduler.deleteJobs(jobKeys);
            System.out.println("剩余任务数：" + scheduler.getJobKeys(gm).size());

            List<String> gnames = scheduler.getJobGroupNames();
            for (String gn : gnames) {
                gm = GroupMatcher.groupEquals(gn);
                System.out.println("任务组【" + gn + "】中任务数：" + scheduler.getJobKeys(gm).size());
            }
        } catch (SchedulerException e) {
            logger.error("failed to delete tasks,", e);
        }
    }

    public void startResTask(Res res) {
        if (res.getPaused() != null && res.getPaused() == 1L) {
            return;
        }
        List<CollectTask> tasks = collectorService.getResCollectTasks(res);
        for (CollectTask t : tasks) {
            //add by wangyl
            if (t.getStatus() == null || t.getStatus().intValue() == 0) {
                continue;
            }
            addTask(res, t);
        }
    }

    public void deleteResTask(Long resId) {
        GroupMatcher<JobKey> gm = GroupMatcher.groupEquals("FIXED_" + resId);
        try {
            scheduler.getJobKeys(gm);
            List<JobKey> jobKeys = new ArrayList<JobKey>();
            jobKeys.addAll(scheduler.getJobKeys(gm));

            scheduler.deleteJobs(jobKeys);
        } catch (SchedulerException e) {
            logger.error("failed to delete task,resId:" + resId, e);
        }
    }

    public void restartResTask(Long resId) {
        deleteResTask(resId);
        Res res = resCache.get(resId);
        if (res != null) {
            startResTask(res);
        }
    }

    public void addTemporaryResTasks(Res res) {
        List<CollectTask> tasks = collectorService.getResCollectTasks(res);
        for (CollectTask t : tasks) {
            if (t.getStatus().intValue() == 0) {
                continue;
            }
            Collector c = collectorCache.get(t.getCollectorId());
            if (c != null && c.getRealTime() != null && 0 == c.getRealTime().intValue()) {//0 表示不能实时采集，不能实时采集的不加入调度
                continue;
            }
            addTemporaryTask(res, t);
        }
    }

    public int getFixTaskCount() {
        GroupMatcher<JobKey> gm = GroupMatcher.groupStartsWith("FIXED_");
        try {
            scheduler.getJobKeys(gm);
            List<JobKey> jobKeys = new ArrayList<JobKey>();
            jobKeys.addAll(scheduler.getJobKeys(gm));
            return scheduler.getJobKeys(gm).size();
        } catch (SchedulerException e) {
            logger.error("failed to get task size,", e);
            return -1;
        }
    }

    /**
     * 
     * @return
     * @throws Exception
     */
    public synchronized List<JobExecutionContext> getCurrentlyExecutingJobs() throws Exception {
        return this.scheduler.getCurrentlyExecutingJobs();
    }

    private void addTask(Res res, CollectTask task) {
        String jobName = "FIXED_" + res.getId() + "_" + task.getId();
        String groupName = "FIXED_" + res.getId();

        JobDetail job = newJob(CollectJob.class).withIdentity(jobName, groupName).build();
        job.getJobDataMap().put(CollectJob.JOB_KEY_RESID, res.getId());
        job.getJobDataMap().put(CollectJob.JOB_KEY_TASKID, task.getId());
        job.getJobDataMap().put("IS_TEMPORARY", false);
        try {
            scheduler.addJob(job, true);
        } catch (SchedulerException e) {
            logger.error("failed to add task job:" + jobName, e);
        }
        List<CollectSchedule> schedules = collectorService.getCollectTaskSchedules(task.getId());
        if (schedules != null) {
            for (CollectSchedule s : schedules) {
                String triggerName = jobName + "_" + s.getId();
                Trigger trigger = makeTrggier(job.getKey(), triggerName, s.getScheduleType(), s.getScheduleParam());
                if (trigger != null) {
                    try {
                        scheduler.scheduleJob(trigger);
                    } catch (Exception e) {
                        logger.error("failed to add task trigger:" + triggerName, e);
                    }
                }
            }
        }
    }

    private Trigger makeTrggier(JobKey jobKey, String triggerName, Long type, String parameter) {
        Trigger trigger = null;
        if (type == PropertyConstants.PROPERTY_REFRESH_PERIOD) {
            int interval = Integer.parseInt(parameter);
            trigger = newTrigger().withSchedule(simpleSchedule().withIntervalInSeconds(interval).repeatForever()).forJob(jobKey).withIdentity(triggerName).startAt(getJobStartTime()).build();
            trigger.getJobDataMap().put(JOB_KEY_INTERVAL, interval);
        } else if (type == PropertyConstants.PROPERTY_REFRESH_TIMER) {
            try {
                trigger = newTrigger().withSchedule(cronSchedule(parameter)).forJob(jobKey).withIdentity(triggerName).build();
                trigger.getJobDataMap().put(JOB_KEY_INTERVAL, "0");
            } catch (Exception e) {
                logger.error("property refresh timing trggier expression error:" + triggerName, e);
                return null;
            }
        } else {
            return null;
        }
        return trigger;
    }

    private void addTemporaryTask(Res res, CollectTask task) {
        String jobName = "TEMP_" + res.getId() + "_" + task.getId();
        String groupName = "TEMPORARY";
        JobDetail job = newJob(CollectJob.class).withIdentity(jobName, groupName).build();
        job.getJobDataMap().put(JOB_KEY_INTERVAL, 10);
        job.getJobDataMap().put("IS_TEMPORARY", true);
        job.getJobDataMap().put(CollectJob.JOB_KEY_RESID, res.getId());
        job.getJobDataMap().put(CollectJob.JOB_KEY_TASKID, task.getId());
        try {
            scheduler.addJob(job, true);
            scheduler.triggerJob(job.getKey());
        } catch (SchedulerException e) {
            logger.error(e);
        }
    }

    private Date getJobStartTime() {
        Calendar cl = Calendar.getInstance();
        double d = Math.random();
        if (d == 0.0)
            d = Math.random();
        cl.setTimeInMillis(cl.getTimeInMillis() + (long) (d * 300 * 1000L));
        return cl.getTime();
    }

    public void killSuspendedTask() {
        try {
            List<JobExecutionContext> jobs = scheduler.getCurrentlyExecutingJobs();
            long curr = new Date().getTime();
            for (int i = 0; i < jobs.size(); i++) {
                try {
                    JobExecutionContext context = jobs.get(i);
                    JobDataMap dataMap = context.getJobDetail().getJobDataMap();
                    Long resId = dataMap.getLong("JOB_KEY_RESID");
                    Long collTaskId = dataMap.getLong("JOB_KEY_TASKID");
                    long begin = dataMap.getLong("begin");
                    if ((curr - begin) / (1000d) > 10 * 60 * 1000) {//任务执行超过10分钟
                        restartResTask(resId);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        } catch (Exception e) {
            logger.error(e);
        }

    }
}
